﻿
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using EventArgs = System.EventArgs;
using OpenTK.Graphics;
using OpenTK;
using LightCAD.Three;
using LightCAD.Three.OpenGL;
using System.Threading;
using LightCAD.Runtime;
using Constants = LightCAD.Three.Constants;
using SkiaSharp;
using LightCAD.Core;

namespace LightCAD.UI
{
    public partial class Model3DEditControl : UserControl, IModel3DEditControl
    {
        private Runtime.IWindow hostWin;
        //[SuppressUnmanagedCodeSecurity]
        //[DllImport("OPENGL32.DLL", EntryPoint = "wglGetProcAddress", ExactSpelling = true, SetLastError = true)]
        //internal extern static IntPtr GetProcAddress(String lpszProc);
        [SuppressUnmanagedCodeSecurity]
        [DllImport("OPENGL32.DLL", EntryPoint = "wglGetProcAddress", ExactSpelling = true, SetLastError = true)]
        internal extern static IntPtr GetProcAddress(IntPtr lpszProc);

        internal event Action onInit;
        internal event Action onDestory;
        internal event Action<string, MouseEventRuntime> mouseEvent;
        internal event Action<KeyEventRuntime> keyDown;
        internal event Action<KeyEventRuntime> keyUp;
        internal event Action<Bounds> sizeChanged;
        internal event Action  onRender;



        private GLControl glControl;
        public Model3DEditControl()
        {
            InitializeComponent();

            this.glControl = new GLControl();
            this.glControl.VSync = false;
            this.Controls.Add(this.glControl);
            this.glControl.Dock = DockStyle.Fill;
            this.Load += ThreeTestForm_Load;
            this.HandleDestroyed += Model3DEditControl_HandleDestroyed;

            this.glControl.MouseDown += GlControl_MouseDown;
            this.glControl.MouseMove += GlControl_MouseMove;
            this.glControl.MouseUp += GlControl_MouseUp;
            this.glControl.KeyDown += GlControl_KeyDown;
            this.glControl.KeyUp += GlControl_KeyUp;
            this.glControl.MouseWheel += GlControl_MouseWheel;
        }

    

        private void GlControl_KeyUp(object? sender, KeyEventArgs e)
        {
            keyUp?.Invoke(e.ToRuntime());
        }

        private void GlControl_KeyDown(object? sender, KeyEventArgs e)
        {
            keyDown?.Invoke(e.ToRuntime());
        }

        private void GlControl_MouseUp(object? sender, MouseEventArgs e)
        {
            mouseEvent?.Invoke("Up", e.ToRuntime());
        }

        private void GlControl_MouseMove(object? sender, MouseEventArgs e)
        {
            mouseEvent?.Invoke("Move", e.ToRuntime());
        }

        private void GlControl_MouseDown(object? sender, MouseEventArgs e)
        {
            mouseEvent?.Invoke("Down", e.ToRuntime());
        }
        private void GlControl_MouseWheel(object? sender, MouseEventArgs e)
        {
            mouseEvent?.Invoke("Wheel", e.ToRuntime());
        }
        private void Model3DEditControl_HandleDestroyed(object? sender, EventArgs e)
        {
            try
            {
                //必须在this.glControl上下文dispose之前进行gl相关的dispose
                this.onDestory?.Invoke();
                this.glControl.Dispose();
            }catch(Exception ex) { }
        }
        
        public void AttachEvents(Action onInit,Action onDestory,
          Action<string, MouseEventRuntime> mouseEvent,
          Action<KeyEventRuntime> keyDown,
          Action<KeyEventRuntime> keyUp,
          Action<Bounds> sizeChanged,
          Action onRender)
        {
            if (onInit != null) this.onInit += onInit;
            if (onDestory != null) this.onDestory += onDestory;
            if (mouseEvent != null) this.mouseEvent += mouseEvent;
            if (keyDown != null) this.keyDown += keyDown;
            if (keyUp != null) this.keyUp += keyUp;
            if (sizeChanged != null) this.sizeChanged += sizeChanged;
            if (onRender != null) this.onRender += onRender;
        }

        public void DetachEvents(Action onInit, Action onDestory,
            Action<string, MouseEventRuntime> mouseEvent,
            Action<KeyEventRuntime> keyDown,
             Action<KeyEventRuntime> keyUp,
           Action<Bounds> sizeChanged,
            Action onRender)
        {
            if (onInit != null) this.onInit -= onInit;
            if (onDestory != null) this.onDestory -= onDestory;
            if (mouseEvent != null) this.mouseEvent -= mouseEvent;
            if (keyDown != null) this.keyDown -= keyDown;
            if (keyUp != null) this.keyUp -= keyDown;
            if (sizeChanged != null) this.sizeChanged -= sizeChanged;
            if (onRender != null) this.onRender -= onRender;
        }



        private void ThreeTestForm_Load(object? sender, EventArgs e)
        {
            this.hostWin = UIUtils.GetParent<Form>(this) as Runtime.IWindow;
            if (this.hostWin == null)
            {
                throw new Exception("Model3DEditControl can not host in Form without IWindow.");
            }

            var context = GraphicsContext.CurrentContext;
            gl.GetAddressFunc = (fn) =>
            {
                var context_internal = context as IGraphicsContextInternal;
                var ptr = context_internal.GetAddress(fn);
                return ptr;
            };

            this.onInit?.Invoke();

            Control_SizeChanged(null,null);

            this.SizeChanged += Control_SizeChanged;
            //this.renderLoop=Task.Run(Render_Loop);
            this.Paint += Model3DEditControl_Paint;

        }




        //private DateTime lastTime;
        //private int frameIndex;
        //public double FPS;
        //public string TitleExt;
        //public void CalFPS()
        //{
        //    if (lastTime == DateTime.MinValue)
        //    {
        //        lastTime = DateTime.Now;
        //    }
        //    frameIndex++;
        //    var delta = (DateTime.Now - lastTime).TotalSeconds;
        //    if (delta > 1)
        //    {
        //        FPS = Math.Round(frameIndex / delta, 1);
        //        lastTime = DateTime.Now;
        //        frameIndex = 0;
        //    }
        //}

        

        //public bool ShowFps { get; set; }

        //private Task renderLoop;
        //private void Render_Loop()
        //{
        //    while (!App.Current.IsExist && !this.IsStop)
        //    {
        //        Application.DoEvents();
        //        if (this.hostWin.IsActive)
        //        {
        //            this.Invoke(() => {
        //                this.OnRender();
        //            });
        //        }
        //        else
        //        {
        //            Thread.Sleep(10);
        //        }
        //    }
        //}

        private bool inRender;

        Bounds IModel3DEditControl.Bounds => glControl.Bounds.ToRuntime();

        private void Model3DEditControl_Paint(object? sender, PaintEventArgs e)
        {
            if (inRender) return;
            this.inRender = true;
            try
            {
                this.glControl.MakeCurrent();

                //if (ShowFps) CalFPS();

                this.onRender?.Invoke();

                this.glControl.SwapBuffers();

            }
            catch (Exception ex)
            {
            }
            this.inRender = false;
        }

        private void Control_SizeChanged(object? sender, EventArgs e)
        {
            this.sizeChanged?.Invoke(this.Bounds.ToRuntime());
            this.Model3DEditControl_Paint(null, null);
        }

        public void RefreshControl()
        {
            if(this.InvokeRequired)
            {
                this.Invoke(() => { this.Model3DEditControl_Paint(null, null); });
            }
            else
            {
                this.Model3DEditControl_Paint(null,null);
            }
        }
    }
}
